Skip to content

csharp: odata lib - #22384

Open
hugo-syn wants to merge 11 commits into
github:mainfrom
hugo-syn:hugo-syn/csharp-odata-tainted-member
Open

csharp: odata lib #22384
hugo-syn wants to merge 11 commits into
github:mainfrom
hugo-syn:hugo-syn/csharp-odata-tainted-member

Conversation

@hugo-syn

Copy link
Copy Markdown

hugo-syn and others added 3 commits August 19, 2026 15:30
Adds semmle.code.csharp.frameworks.OData, following the WCF.qll/JsonNET.qll
convention: values cast, as-converted, or type-tested out of an untyped
ODataActionParameters dictionary, and entities tracked by Delta<T> (via
GetInstance/Patch/Put/CopyChangedValues/CopyUnchangedValues), have no static
type relationship to the action method's own parameter types, so their
members aren't picked up by the existing AspNetRemoteFlowSourceMember
modeling. This adds a TaintedMember for those bound types (with the same
nested-type/collection recursion as AspNetRemoteFlowSourceMember), plus two
AdditionalTaintStep steps for the Delta<T> method calls, which don't fit the
member-read shape TaintedMember covers.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Match WCF.qll's convention: only the TaintedMember/AdditionalTaintStep
wiring classes stay private, everything else that identifies a reusable
OData domain concept (ODataActionParametersClass, DeltaClass,
ODataBoundType, DeltaMutatingMethod, DeltaGetInstanceMethod) is public.

Also renames the test fixtures to generic placeholder names.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
import csharp already publicly imports semmle.code.csharp.dataflow.TaintTracking
(and DataFlow), same as WCF.qll/JsonNET.qll rely on implicitly.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@hugo-syn
hugo-syn requested a review from a team as a code owner August 19, 2026 13:57

@michaelnebel michaelnebel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much! It is really good, if we can get our modelling extended even further!

I have added some initial comments / questions. Maybe OData parameter like types are only relevant for classes that extend ODataController. Should that somehow be incorporated in the logic?

Comment on lines +152 to +160
private class DeltaGetInstanceTaintStep extends AdditionalTaintStep {
override predicate step(DataFlow::Node node1, DataFlow::Node node2) {
exists(MethodCall mc |
mc.getTarget().getUnboundDeclaration() instanceof DeltaGetInstanceMethod and
node1.asExpr() = mc.getQualifier() and
node2.asExpr() = mc
)
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps, the QL implementation can be replaced by Models as Data?
Below is the row for one of the GetInstance methods.

extensions:
  - addsTo:
      pack: codeql/csharp-all
      extensible: summaryModel
    data:
      - ["Microsoft.AspNet.OData", "Delta<TStructuralType>", True, "GetInstance", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +3 to +25
namespace Microsoft.AspNet.OData
{
public class ODataActionParameters : Dictionary<string, object>
{
}

public class Delta<TStructuralType> where TStructuralType : class
{
private TStructuralType instance;

public Delta() { instance = default(TStructuralType); }

public TStructuralType GetInstance() => instance;

public void Patch(TStructuralType original) { }

public void Put(TStructuralType original) { }

public void CopyChangedValues(TStructuralType original) { }

public void CopyUnchangedValues(TStructuralType original) { }
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ideally, we would like to keep stub implementations separate from the test and store them in test/resources/stubs.
This will require an options file for the test; If possible, it is also preferred, if the test relies fully on stubs and not any .dll files.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment thread csharp/ql/lib/semmle/code/csharp/frameworks/OData.qll Outdated
TaintTracking::localExprTaint(any(ODataActionParameterRead r), e)
}

/** The generic `Delta<TStructuralType>` change-tracking class, across OData library versions. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe refer to the unbound declaration with "Delta1" instead of Delta<TStructuralType> as the type parameter is named T for Microsoft.AspNetCore.OData.Deltas.Delta<T>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

hugo-syn added 4 commits August 21, 2026 09:51
Per review feedback on github#22384, replace the hand-written
DeltaGetInstanceMethod/DeltaGetInstanceTaintStep taint step with a
Models-as-Data summaryModel row for both the Microsoft.AspNet.OData and
Microsoft.AspNetCore.OData.Deltas variants of Delta<T>.GetInstance().
Per review feedback on github#22384, OData.qll's CandidateODataMember was an
exact copy of CandidateMemberToTaint from Remote.qll. Make that class
public and import it instead of duplicating it.
Per review feedback on github#22384, keep the ODataActionParameters/Delta<T>
stub implementations out of the test .cs file and store them in
test/resources/stubs instead, following the pattern used by other
frameworks (e.g. JsonNET, Aws). The test now loads the stub project
via an options file and relies on no .dll files.
Per review feedback on github#22384, the doc comment named the type
parameter TStructuralType, but the AspNetCore variant of Delta<T>
names it T. Refer to the unbound generic as \`Delta\`1\`\` instead.
@hugo-syn

Copy link
Copy Markdown
Author

Hi @michaelnebel I think I've made changes for all your requests let me know if it's ok

I'm not sure to get you question:

Maybe OData parameter like types are only relevant for classes that extend ODataController. Should that somehow be incorporated in the logic?

Can you give more details / examples ?

@jzabroski

Copy link
Copy Markdown
Contributor

Maybe OData parameter like types are only relevant for classes that extend ODataController.

I don't agree with this advice.

OData can work without inheriting from ODataController by using standard ASP.NET Core Controller or ApiController classes combined with the [EnableQuery] attribute or manual ODataQueryOptions parsing.

Therefore, the filter to speed up CodeQL database matches should have all 3 possibilities:

  1. Extend ODataController; or
  2. Extend Controller; or
  3. Extend ApiController

For the latter two, either

  1. [EnableQuery] attribute; or
  2. ODataQueryOptions detected

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

⚠️ The head of this PR and the base branch were compared for differences in the framework coverage reports. The generated reports are available in the artifacts of this workflow run. The differences will be picked up by the nightly job after the PR gets merged.

Click to show differences in coverage

csharp

Generated file changes for csharp

  • Changes to framework-coverage-csharp.rst:
-    Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2406,162,4
+    Others,"``Amazon.Lambda.APIGatewayEvents``, ``Amazon.Lambda.Core``, ``Dapper``, ``ILCompiler``, ``ILLink.RoslynAnalyzer``, ``ILLink.Shared``, ``ILLink.Tasks``, ``Internal.IL``, ``Internal.Pgo``, ``Internal.TypeSystem``, ``Microsoft.ApplicationBlocks.Data``, ``Microsoft.AspNet.OData``, ``Microsoft.AspNetCore.Components``, ``Microsoft.AspNetCore.Http``, ``Microsoft.AspNetCore.Mvc``, ``Microsoft.AspNetCore.OData.Deltas``, ``Microsoft.AspNetCore.WebUtilities``, ``Microsoft.CSharp``, ``Microsoft.Data.SqlClient``, ``Microsoft.Diagnostics.Tools.Pgo``, ``Microsoft.DotNet.Build.Tasks``, ``Microsoft.DotNet.PlatformAbstractions``, ``Microsoft.EntityFrameworkCore``, ``Microsoft.Extensions.Caching.Distributed``, ``Microsoft.Extensions.Caching.Memory``, ``Microsoft.Extensions.Configuration``, ``Microsoft.Extensions.DependencyInjection``, ``Microsoft.Extensions.DependencyModel``, ``Microsoft.Extensions.Diagnostics.Metrics``, ``Microsoft.Extensions.FileProviders``, ``Microsoft.Extensions.FileSystemGlobbing``, ``Microsoft.Extensions.Hosting``, ``Microsoft.Extensions.Http``, ``Microsoft.Extensions.Logging``, ``Microsoft.Extensions.Options``, ``Microsoft.Extensions.Primitives``, ``Microsoft.Interop``, ``Microsoft.JSInterop``, ``Microsoft.NET.Build.Tasks``, ``Microsoft.VisualBasic``, ``Microsoft.Win32``, ``Mono.Linker``, ``MySql.Data.MySqlClient``, ``NHibernate``, ``Newtonsoft.Json``, ``SourceGenerators``, ``Windows.Security.Cryptography.Core``",60,2416,162,4
-    Totals,,108,14908,415,9
+    Totals,,108,14918,415,9
  • Changes to framework-coverage-csharp.csv:
+ Microsoft.AspNet.OData,,,5,,,,,,,,,,,,,,,,,,,5,
+ Microsoft.AspNetCore.OData.Deltas,,,5,,,,,,,,,,,,,,,,,,,5,

@michaelnebel michaelnebel left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am sorry for the delay in review; Thank you for your diligence @hugo-syn.
Will also start a DCA run (automated testing against a set of repositories)

@@ -0,0 +1,19 @@
// This file contains auto-generated code.
// Generated from `Microsoft.AspNet.OData, Version=7.7.5.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35`.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this indeed auto-generated? Did you use the make_stubs_nuget.py to generate the file?

If it is not auto-generated, could you then move this file to csharp/ql/test/resources/stubs (and then remove comments about code being auto generated)?
If it is auto generated, then please leave it here (sorry about being a bit pushy about this - otherwise I will be really confused when trying to update all stubs later in the future) and then add the package to the list in make_stubs_all.py.


/** Holds if `e` may (locally) hold the value of an `ODataActionParameters` entry. */
private predicate isODataParameterValue(Expr e) {
TaintTracking::localExprTaint(any(ODataActionParameterRead r), e)

@michaelnebel michaelnebel Aug 25, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
TaintTracking::localExprTaint(any(ODataActionParameterRead r), e)
DataFlow::localExprFlow(any(ODataActionParameterRead r), e)

Maybe we should consider using local data flow instead (and not only taint tracking), then it becomes a bit more strict, which types we consider to be ODataBound (and it appears that all test-cases pass). Or do you know of a real world example, where this wouldn't be good enough?

Comment thread csharp/ql/lib/ext/Microsoft.AspNet.OData.model.yml
Comment thread csharp/ql/lib/semmle/code/csharp/frameworks/OData.qll Outdated
Comment thread csharp/ql/lib/semmle/code/csharp/frameworks/OData.qll Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds OData action-parameter and Delta<T> taint tracking to the C# analysis libraries.

Changes:

  • Models OData-bound types, members, and mutating Delta operations.
  • Adds GetInstance flow summaries.
  • Adds classic OData test fixtures and release notes.
Show a summary per file
File Description
Microsoft.AspNet.OData.csproj Configures the test stub project.
Microsoft.AspNet.OData.cs Provides generated OData API stubs.
OData/options Loads the OData test stubs.
OData/OData.ql Defines the taint test query.
OData/OData.expected Records expected flows.
OData/OData.cs Exercises dictionary and Delta flows.
Remote.qll Exposes the reusable member candidate class.
OData.qll Implements OData taint modeling.
TaintTrackingPrivate.qll Registers the OData models.
Microsoft.AspNet.OData.model.yml Models GetInstance return flow.
2026-08-19-odata-taint-step.md Documents the feature.

Review details

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

  • Files reviewed: 11/11 changed files
  • Comments generated: 4
  • Review effort level: Balanced

Comment on lines +50 to +51
this.hasFullyQualifiedName("Microsoft.AspNet.OData", "Delta`1") or
this.hasFullyQualifiedName("Microsoft.AspNetCore.OData.Deltas", "Delta`1")
Comment on lines +40 to +42
/** Holds if `e` may (locally) hold the value of an `ODataActionParameters` entry. */
private predicate isODataParameterValue(Expr e) {
TaintTracking::localExprTaint(any(ODataActionParameterRead r), e)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is used for constructing an approximation of a set of types where we want to taint the members - maybe the existing implementation suffices for most real world examples (I will leave it to you @hugo-syn , if you want to improve further - IMO this is not something that blocks the current PR)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure so I've added it just in case

pack: codeql/csharp-all
extensible: summaryModel
data:
- ["Microsoft.AspNet.OData", "Delta<TStructuralType>", True, "GetInstance", "()", "", "Argument[this]", "ReturnValue", "taint", "manual"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They return void.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added both cases

Comment on lines +29 to +31
this.hasFullyQualifiedName("Microsoft.AspNet.OData", "ODataActionParameters") or
this.hasFullyQualifiedName("Microsoft.AspNetCore.OData.Formatter", "ODataActionParameters") or
this.hasFullyQualifiedName("System.Web.Http.OData", "ODataActionParameters")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests are already acceptable.

hugo-syn and others added 4 commits August 25, 2026 15:04
Co-authored-by: Michael Nebel <michaelnebel@github.com>
Co-authored-by: Michael Nebel <michaelnebel@github.com>
Co-authored-by: Michael Nebel <michaelnebel@github.com>
@hugo-syn

Copy link
Copy Markdown
Author

Hey @michaelnebel I've taken into account comments from Copilot and your comments let me know if its better

@hugo-syn

Copy link
Copy Markdown
Author

Also curious about the DCA, do you have a list of projects using OData ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants